<script lang="tsx">
  import type { PropType, Ref } from "vue"
  import type { FormActionType, FormProps } from "../types/form"
  import type { FormSchema } from "../types/form"
  import type { Rule } from "ant-design-vue/lib/form"
  import type { TableActionType } from "/@/components/Table"
  import { defineComponent, computed, unref, toRefs } from "vue"
  import { Form, Col, Divider } from "ant-design-vue"
  import { componentMap } from "../componentMap"
  import { BasicHelp } from "/@/components/Basic"
  import { isBoolean, isEmpty, isFunction, isNull, isNullOrUnDef } from "/@/utils/is"
  import { getSlot } from "/@/utils/helper/tsxHelper"
  import { createPlaceholderMessage, setComponentRuleType } from "../helper"
  import { upperFirst, cloneDeep } from "lodash-es"
  import { useItemLabelWidth } from "../hooks/useLabelWidth"
  import { useI18n } from "/@/hooks/web/useI18n"

  export default defineComponent({
    name: "BasicFormItem",
    inheritAttrs: false,
    props: {
      schema: {
        type: Object as PropType<FormSchema>,
        default: () => ({}),
      },
      formProps: {
        type: Object as PropType<FormProps>,
        default: () => ({}),
      },
      allDefaultValues: {
        type: Object as PropType<Recordable>,
        default: () => ({}),
      },
      formModel: {
        type: Object as PropType<Recordable>,
        default: () => ({}),
      },
      setFormModel: {
        type: Function as PropType<(key: string, value: any, labelKey?: string, labelValue?: any) => void>,
        default: null,
      },
      tableAction: {
        type: Object as PropType<TableActionType>,
      },
      formActionType: {
        type: Object as PropType<FormActionType>,
      },
    },
    setup(props, { slots }) {
      const { t } = useI18n()

      const { schema, formProps } = toRefs(props) as {
        schema: Ref<FormSchema>
        formProps: Ref<FormProps>
      }

      const itemLabelWidthProp = useItemLabelWidth(schema, formProps)

      const getValues = computed(() => {
        const { allDefaultValues, formModel, schema } = props
        const { mergeDynamicData } = props.formProps
        return {
          field: schema.field,
          model: formModel,
          values: {
            ...mergeDynamicData,
            ...allDefaultValues,
            ...formModel,
          } as Recordable,
          schema: schema,
        }
      })

      const getComponentsProps = computed(() => {
        const { schema, formModel, tableAction, formActionType } = props
        let { componentProps = {} } = schema
        if (isFunction(componentProps)) {
          componentProps = componentProps({ schema, formModel, tableAction, formActionType }) ?? {}
        }
        if (schema.component === "InputPassword" && !componentProps["autocomplete"]) {
          componentProps["autocomplete"] = "false"
        }
        if (schema.component === "Divider") {
          componentProps = Object.assign(
            { type: "horizontal" },
            {
              orientation: "left",
              plain: true,
            },
            componentProps
          )
        }
        return componentProps as Recordable
      })

      const getDisable = computed(() => {
        const { disabled: globDisabled } = props.formProps
        const { dynamicDisabled } = props.schema
        const { disabled: itemDisabled = false } = unref(getComponentsProps)
        let disabled = !!globDisabled || itemDisabled
        if (isBoolean(dynamicDisabled)) {
          disabled = dynamicDisabled
        }
        if (isFunction(dynamicDisabled)) {
          disabled = dynamicDisabled(unref(getValues))
        }
        return disabled
      })

      function getShow(): { isShow: boolean; isIfShow: boolean } {
        const { show, ifShow } = props.schema
        const { showAdvancedButton } = props.formProps
        const itemIsAdvanced = showAdvancedButton ? (isBoolean(props.schema.isAdvanced) ? props.schema.isAdvanced : true) : true

        let isShow = true
        let isIfShow = true

        if (isBoolean(show)) {
          isShow = show
        }
        if (isBoolean(ifShow)) {
          isIfShow = ifShow
        }
        if (isFunction(show)) {
          isShow = show(unref(getValues))
        }
        if (isFunction(ifShow)) {
          isIfShow = ifShow(unref(getValues))
        }
        isShow = isShow && itemIsAdvanced
        return { isShow, isIfShow }
      }

      function handleRules(): Rule[] {
        const { rules: defRules = [], component, rulesMessageJoinLabel, label, dynamicRules, required } = props.schema

        if (isFunction(dynamicRules)) {
          return dynamicRules(unref(getValues)) as Rule[]
        }

        let rules: Rule[] = cloneDeep(defRules) as Rule[]
        const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.formProps

        const joinLabel = Reflect.has(props.schema, "rulesMessageJoinLabel") ? rulesMessageJoinLabel : globalRulesMessageJoinLabel
        const defaultMsg = createPlaceholderMessage(component) + `${joinLabel ? label : ""}`

        function validator(rule: any, value: any) {
          const msg = rule.message || defaultMsg
          if (value === undefined || isNull(value)) {
            // 空值
            return Promise.reject(msg)
          } else if (Array.isArray(value) && value.length === 0) {
            // 数组类型
            return Promise.reject(msg)
          } else if (typeof value === "string" && value.trim() === "") {
            // 空字符串
            return Promise.reject(msg)
          } else if (
            typeof value === "object" &&
            Reflect.has(value, "checked") &&
            Reflect.has(value, "halfChecked") &&
            Array.isArray(value.checked) &&
            Array.isArray(value.halfChecked) &&
            value.checked.length === 0 &&
            value.halfChecked.length === 0
          ) {
            // 非关联选择的tree组件
            return Promise.reject(msg)
          }
          return Promise.resolve()
        }

        const getRequired = isFunction(required) ? required(unref(getValues)) : required

        /*
         * 1、若设置了required属性，又没有其他的rules，就创建一个验证规则；
         * 2、若设置了required属性，又存在其他的rules，则只rules中不存在required属性时，才添加验证required的规则
         *     也就是说rules中的required，优先级大于required
         */
        if (getRequired) {
          if (!rules || rules.length === 0) {
            rules = [{ required: getRequired, validator }]
          } else {
            const requiredIndex: number = rules.findIndex((rule) => Reflect.has(rule, "required"))

            if (requiredIndex === -1) {
              rules.push({ required: getRequired, validator })
            }
          }
        }

        const requiredRuleIndex: number = rules.findIndex((rule) => Reflect.has(rule, "required") && !Reflect.has(rule, "validator"))

        if (requiredRuleIndex !== -1) {
          const rule = rules[requiredRuleIndex]
          const { isShow } = getShow()
          if (!isShow) {
            rule.required = false
          }
          // rules: [{ required: true }] 时的验证应与直接写 required: true 的验证相同
          if (rule.required && !rule.validator) {
            rule.validator = validator
          }
          if (component) {
            if (!Reflect.has(rule, "type")) {
              rule.type = component === "InputNumber" ? "number" : "string"
            }

            rule.message = rule.message || defaultMsg

            if (component.includes("Input") || component.includes("Textarea")) {
              rule.whitespace = true
            }
            const valueFormat = unref(getComponentsProps)?.valueFormat
            setComponentRuleType(rule, component, valueFormat)
          }
        }

        // Maximum input length rule check
        const characterInx = rules.findIndex((val) => val.max)
        if (characterInx !== -1 && !rules[characterInx].validator) {
          rules[characterInx].message = rules[characterInx].message || t("component.form.maxTip", [rules[characterInx].max] as Recordable)
        }
        return rules
      }

      function renderComponent() {
        const {
          renderComponentContent,
          component,
          field,
          fieldLabel,
          changeEvent = "change",
          valueField,
          labelField,
          defaultValue,
          defaultLabel,
        } = props.schema

        const isCheck = component && ["Switch", "Checkbox"].includes(component)

        const eventKey = `on${upperFirst(changeEvent)}`

        const on = {
          [eventKey]: (...args: Nullable<Recordable<any>>[]) => {
            const [e] = args
            if (propsData[eventKey]) {
              propsData[eventKey](...args)
            }
            const target = e ? e.target : null
            const value = target ? (isCheck ? target.checked : target.value) : e
            props.setFormModel(field, value, props.schema)
          },
        }
        const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>

        const { autoSetPlaceHolder, size } = props.formProps
        const propsData: Recordable<any> = {
          // allowClear: true, // 默认不显示清除按钮
          getPopupContainer: () => document.body, //(trigger: Element) => trigger.parentNode, 会导致 RangePicker 选择问题
          size,
          ...unref(getComponentsProps),
          disabled: unref(getDisable),
        }

        const isCreatePlaceholder = !propsData.disabled && autoSetPlaceHolder
        // RangePicker place is an array
        if (isCreatePlaceholder && component !== "RangePicker" && component) {
          propsData.placeholder = unref(getComponentsProps)?.placeholder || createPlaceholderMessage(component)
        }
        propsData.codeField = field
        propsData.formValues = unref(getValues)

        let formValue = props.formModel[field]
        if (component === "RangePicker" && formValue === "") {
          formValue = []
        }

        const bindValue: Recordable = {
          [valueField || (isCheck ? "checked" : "value")]: formValue,
        }

        if (fieldLabel) {
          bindValue[labelField || "labelValue"] = props.formModel[fieldLabel] || defaultLabel
          // console.log('bindValue', bindValue, props.formModel);
          bindValue.labelInValue = true
          // bindValue.treeCheckable = true;
        }

        const compAttr: Recordable = {
          ...propsData,
          ...on,
          ...bindValue,
        }

        if (!renderComponentContent) {
          return <Comp {...compAttr} />
        }
        const compSlot = isFunction(renderComponentContent)
          ? { ...renderComponentContent(unref(getValues)) }
          : {
              default: () => renderComponentContent,
            }
        return <Comp {...compAttr}>{compSlot}</Comp>
      }

      /**
       *渲染Label
       */
      function renderLabelHelpMessage() {
        // 表单超出4个 .. 省略显示
        //label宽度支持自定义
        const { label, helpMessage, helpComponentProps, subLabel, labelLength, itemProps = {} } = props.schema
        const helpPosition = !itemProps.helpPosition ? 'label' : itemProps.helpPosition
        let showLabel: string = label + ""
        if (labelLength && showLabel.length > 4) {
          showLabel = showLabel.substr(0, labelLength)
        }
        const titleObj = { title: label }
        const renderLabel = subLabel ? (
          <span>
            {label} <span class="text-secondary">{subLabel}</span>
          </span>
        ) : labelLength ? (
          <label {...titleObj}>{showLabel}</label>
        ) : (
          label
        )
        const getHelpMessage = isFunction(helpMessage) ? helpMessage(unref(getValues)) : helpMessage
        if (helpPosition == 'item' || (!getHelpMessage || (Array.isArray(getHelpMessage) && getHelpMessage.length === 0))) {
          return renderLabel
        }
        return (
          <span>
            {renderLabel}
            <BasicHelp placement="top" text={getHelpMessage} {...helpComponentProps} />
          </span>
        )
      }

      function renderItemHelpMessage() {
        const { helpMessage, itemProps = {} } = props.schema
        const getHelpMessage = isFunction(helpMessage) ? helpMessage(unref(getValues)) : helpMessage

        if (itemProps?.helpPosition == 'item') {
          return (
            <div>
              <Form.Item  label=" " colon={false} v-show={true} >
                <span class="form-item-help">{getHelpMessage}</span>
              </Form.Item>
      
            </div>
          )
        } else {
          return (
            <p></p>
          )
        }
      }


      function renderItem() {
        const { itemProps, useFormItemWrapper, slot, render, label, field, helpMessage, fieldLabel, suffix, component } = props.schema
        const { labelCol, wrapperCol } = unref(itemLabelWidthProp)
        const { colon } = props.formProps
        if (component === "None") {
          return "" // 占位符，什么也不输出
        } else if (component === "Divider" || component === "FormGroup") {
          const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>
          return (
            <Col span={24}>
              <Comp {...unref(getComponentsProps)}>{renderLabelHelpMessage()}</Comp>
            </Col>
          )
        } else {
          const getContent = () => {
            return slot ? getSlot(slots, slot, unref(getValues)) : render ? render(unref(getValues)) : renderComponent()
          }

          const showSuffix = !!suffix
          const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix
          if (useFormItemWrapper === false) {
            return (
              <div>
                {showSuffix ? (
                  <div style="display: flex">
                    <div style="flex: 1">{getContent()}</div>
                    <span class="suffix">{getSuffix}</span>
                  </div>
                ) : (
                  getContent()
                )}
              </div>
            )
          }

          return (
           <div>
              <Form.Item
                name={field}
                colon={itemProps?.colon || colon}
                class={{ "suffix-item": showSuffix, "no-label": isEmpty(label) }}
                {...(itemProps as Recordable)}
                label={renderLabelHelpMessage()}
                rules={handleRules()}
                labelCol={labelCol}
                wrapperCol={wrapperCol}
              >
                {showSuffix ? (
                  <div style="display: flex">
                    <div style="flex: 1">{getContent()}</div>
                    <span class="suffix">{getSuffix}</span>
                  </div>
                ) : (
                  getContent()
                )}
                {fieldLabel ? <Form.Item name={fieldLabel} v-show={false} /> : null}
              </Form.Item>
              { renderItemHelpMessage() }
            </div>
          )
        }
      }

      return () => {
        const { colProps = {}, colSlot, renderColContent, component } = props.schema
        if (!componentMap.has(component)) {
          return null
        }

        const { baseColProps = {} } = props.formProps
        const realColProps = { ...baseColProps, ...colProps }
        const { isIfShow, isShow } = getShow()
        const values = unref(getValues)

        const getContent = () => {
          return colSlot ? getSlot(slots, colSlot, values) : renderColContent ? renderColContent(values) : renderItem()
        }

        return (
          isIfShow && (
            <Col {...realColProps} v-show={isShow}>
              {getContent()}
            </Col>
          )
        )
      }
    },
  })
</script>
